Startup

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6     ✔ purrr   0.3.4
✔ tibble  3.1.4     ✔ dplyr   1.0.7
✔ tidyr   1.1.3     ✔ stringr 1.4.0
✔ readr   2.0.1     ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(cowplot)
library(ggpmisc)
Loading required package: ggpp

Attaching package: ‘ggpp’

The following object is masked from ‘package:ggplot2’:

    annotate
library(here)
here() starts at /labs/khatrilab/solomonb/hla_project/hla_benchmark
source(here("helper_functions/data_import_functions.R"))
source(here("helper_functions/figure_format_functions.R"))
Loading required package: flextable
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio

Attaching package: ‘flextable’

The following object is masked from ‘package:purrr’:

    compose
source(here("helper_functions/calculation_functions.R"))
Loading required package: tidymodels
Registered S3 method overwritten by 'tune':
  method                   from   
  required_pkgs.model_spec parsnip
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidymodels 0.1.3 ──
✔ broom        0.7.9      ✔ rsample      0.1.0 
✔ dials        0.0.9      ✔ tune         0.1.6 
✔ infer        1.0.0      ✔ workflows    0.2.3 
✔ modeldata    0.1.1      ✔ workflowsets 0.1.0 
✔ parsnip      0.1.7      ✔ yardstick    0.0.8 
✔ recipes      0.1.16     
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidymodels_conflicts() ──
✖ ggpp::annotate()     masks ggplot2::annotate()
✖ flextable::compose() masks purrr::compose()
✖ scales::discard()    masks purrr::discard()
✖ dplyr::filter()      masks stats::filter()
✖ recipes::fixed()     masks stringr::fixed()
✖ dplyr::lag()         masks stats::lag()
✖ yardstick::spec()    masks readr::spec()
✖ recipes::step()      masks stats::step()
• Use tidymodels_prefer() to resolve common conflicts.
Loading required package: checkmate

Attaching package: ‘checkmate’

The following object is masked from ‘package:recipes’:

    check_class

Loading required package: lubridate

Attaching package: ‘lubridate’

The following object is masked from ‘package:cowplot’:

    stamp

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
isb_path <- here("data/isb")
isb_samples <- read_tsv(here("data/isb/logs/parallel.log")) %>% 
  separate(Command, into = c(NA, "sample"), sep = " ") %>% 
  pull(sample) %>% unique()
Rows: 294 Columns: 9
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): Host, Command
dbl (7): Seq, Starttime, JobRuntime, Send, Receive, Exitval, Signal

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Get reference alleles

reference_alleles <- read_tsv(here("references/GCh38_reference_alleles.txt"), col_names = F) %>% pull(1)
Rows: 26 Columns: 1
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): X1

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
genome_region_reference <- get_allele_length(reference_alleles)

Get arcasHLA alignment stats

arcas_log_dir <- sprintf("%s/arcasHLA", isb_path)
alignment_stats_df <- hla_mapping_stats_import(isb_samples, arcas_log_dir)

Import cell counts

cell_counts_df <- readRDS(here("data/isb/isb_sequenced_cell_count.RDS"))

Assemble accuracy DF

success_df <- readRDS(here("2_accuracy/isb_success.RDS"))
accuracy_df <- readRDS(here("3_DRB/isb_accuracy_drb345_filtered.RDS"))
accuracy_df <- accuracy_df %>% 
  left_join(
    success_df %>% select(sample:field, genotyper, success = accuracy),
    by = c("sample", "locus", "field", "genotyper")
  )%>% 
  left_join(alignment_stats_df, by = c("sample", "locus")) %>% 
  left_join(genome_region_reference, by = "locus") %>%
  left_join(cell_counts_df, by = "sample") %>%
  mutate_at(vars(c(reads, bp)), as.numeric) %>% 
  mutate(coverage = (reads * 91)/bp) %>% 
  mutate(n_alleles = map_dbl(allele, function(x) sum(!is.na(x)))) %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)$", sample) & genotyper != "hlaminer") 

Coverage based plots

gg_coverage(accuracy_df, x_var = "coverage", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "coverage", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "coverage", y_var = "n_alleles", facet_genotyper = T)

Cell number based plots

gg_coverage(accuracy_df, x_var = "n_cells", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "n_cells", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)

Subsample READS

Import read subsample data

# Import predicted genotypes
subsample_reads_path <- here("data/isb/subsample")
subsample_reads_samples <- tibble(sample = list.files(sprintf("%s/arcasHLA", subsample_reads_path))) %>% 
  filter(grepl("^INCOV", sample)) %>% 
  separate(sample, into = c("sample"), sep = "\\.", extra = "drop") %>% 
  distinct() %>% 
  pull(sample)
subsample_reads_df <- format_hla_table(combine_HLA_import(path = subsample_reads_path, samples = subsample_reads_samples))
subsample_reads_df <- subsample_reads_df %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)_", sample))
subsample_reads_df
# Expand invitro genotypes across read subsample names
invitro_df <- invitro_import()
Rows: 1557 Columns: 9
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (8): Sample ID, Locus, Allele 1, Allele 2, Comments, Diploid Ambiguities, Allele 1 Ambiguities, Allele 2 Ambiguities
dbl (1): index

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
invitro_reads_df <- subsample_reads_df %>% 
  select(sample) %>% 
  distinct() %>% 
  separate(sample, into = c("sample", "subsample"), sep = "_") %>% 
  left_join(invitro_df, by = "sample") %>% 
  unite(sample, sample, subsample, sep = "_") %>% 
  format_hla_table() %>% 
  drop_na()
invitro_reads_df
# Import read statistics
subsample_reads_arcas_log_dir <- sprintf("%s/arcasHLA", subsample_reads_path)
subsample_reads_alignment_stats_df <- hla_mapping_stats_import(subsample_reads_samples, subsample_reads_arcas_log_dir)

Calculate accuracy

# Calculate accuracy
subsample_reads_accuracy_df <- compare_hla(
  hla_df = bind_rows(
    subsample_reads_df,
    invitro_reads_df
  ),
  reference = "invitro", 
  method = "accuracy"
)
# saveRDS(subsample_reads_accuracy_df, "isb_subsample_reads_accuracy.RDS")
# Calculate success
subsample_reads_success_df <- compare_hla(
  hla_df = bind_rows(
    subsample_reads_df,
    invitro_reads_df
  ),
  reference = "invitro", 
  method = "success"
)
# saveRDS(subsample_reads_success_df, "isb_subsample_reads_success.RDS")
# Combine various data
subsample_reads_combined_df <- subsample_reads_accuracy_df %>% 
  left_join(
    subsample_reads_success_df %>% select(sample:field, genotyper, success = accuracy),
    by = c("sample", "locus", "field", "genotyper")
  )%>% 
  left_join(subsample_reads_alignment_stats_df, by = c("sample", "locus")) %>% 
  left_join(genome_region_reference, by = "locus") %>%
  bind_rows(accuracy_df %>% select(-n_cells)) %>% # No cell numbers for read subsamples
  mutate_at(vars(c(reads, bp)), as.numeric) %>% 
  mutate(coverage = (reads * 91)/bp) %>%  # Calculate coverage
  mutate(n_alleles = map_dbl(allele, function(x) sum(!is.na(x)))) %>%  # Calculate n_alleles
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)", sample) & genotyper != "hlaminer") 
# saveRDS(subsample_reads_combined_df, "subsample_reads_combined_df.RDS")
# subsample_reads_combined_df <- readRDS("subsample_reads_combined_df.RDS")

Reads plots

gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

plt_read_accuracy_all <- gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "accuracy") 
plt_read_accuracy_abc <- gg_coverage(subsample_reads_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "reads", y_var = "accuracy") 
gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

plt_read_success_all <- gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "success")
plt_read_success_abc <- gg_coverage(subsample_reads_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "reads", y_var = "success") 
gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "n_alleles", facet_genotyper = T)

plt_read_allele_all <- gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "n_alleles", facet_genotyper = T)
plt_read_allele_abc <- gg_coverage(subsample_reads_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "reads", y_var = "n_alleles", facet_genotyper = T)
  • Default arcasHLA cut off 75 reads (log10 = 1.8)
subsample_reads_combined_df %>% 
  filter(field == "field_2") %>% 
  mutate(n_alleles = factor(n_alleles, levels = 0:2)) %>% 
  ggplot(aes(y = n_alleles, x = log10(coverage)))+
  # geom_violin(scale = "count", alpha = 0.5, )+
  geom_jitter(size = 0.2, width = 0, height =0.2, alpha = 0.2)+
  # geom_boxplot(width=0.1)+
  # ggbeeswarm::geom_beeswarm(size = 0.1, alpha = 0.1)+
  # coord_flip()+
  facet_grid(genotyper ~ locus, scales = "free")+
  theme_bw()

Subsample CELLS

# Import predicted genotypes
subsample_cells_path <- here("data/isb/subsample_cells")
subsample_cells_samples <- tibble(sample = list.files(sprintf("%s/arcasHLA", subsample_cells_path))) %>% 
  filter(grepl("^INCOV", sample)) %>% 
  separate(sample, into = c("sample"), sep = "\\.", extra = "drop") %>% 
  distinct() %>% 
  pull(sample)
subsample_cells_df <- format_hla_table(combine_HLA_import(path = subsample_cells_path, samples = subsample_cells_samples))
subsample_cells_df <- subsample_cells_df %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)_", sample))
subsample_cells_df
# Expand invitro genotypes across read subsample names
invitro_df <- invitro_import()
Rows: 1557 Columns: 9
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (8): Sample ID, Locus, Allele 1, Allele 2, Comments, Diploid Ambiguities, Allele 1 Ambiguities, Allele 2 Ambiguities
dbl (1): index

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
invitro_cells_df <- subsample_cells_df %>% 
  select(sample) %>% 
  distinct() %>% 
  separate(sample, into = c("sample", "subsample"), sep = "_") %>% 
  left_join(invitro_df, by = "sample") %>% 
  unite(sample, sample, subsample, sep = "_") %>% 
  format_hla_table() %>% 
  drop_na()
invitro_cells_df
# Import read statistics
subsample_cells_arcas_log_dir <- sprintf("%s/arcasHLA", subsample_cells_path)
subsample_cells_alignment_stats_df <- hla_mapping_stats_import(subsample_cells_samples, subsample_cells_arcas_log_dir)
# Import cell counts for each subsample
subsample_cell_counts_df <- read_csv(here("data/isb/subsample_cells/subsample_cell_counts.csv"))
Rows: 1176 Columns: 2
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): sample
dbl (1): n_cells

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Calculate accuracy

# Calculate accuracy
subsample_cells_accuracy_df <- compare_hla(
  hla_df = bind_rows(
    subsample_cells_df,
    invitro_cells_df
  ),
  reference = "invitro", 
  method = "accuracy"
)
# saveRDS(subsample_cells_accuracy_df, "isb_subsample_cells_accuracy.RDS")
# subsample_cells_accuracy_df <- readRDS("isb_subsample_cells_accuracy.RDS")
# Calculate success
subsample_cells_success_df <- compare_hla(
  hla_df = bind_rows(
    subsample_cells_df,
    invitro_cells_df
  ),
  reference = "invitro", 
  method = "success"
)
# saveRDS(subsample_cells_success_df, "isb_subsample_cells_success.RDS")
# subsample_cells_success_df <- readRDS("isb_subsample_cells_success.RDS")
# Combine various data
subsample_cells_combined_df <- subsample_cells_accuracy_df %>% 
  left_join(
    subsample_cells_success_df %>% select(sample:field, genotyper, success = accuracy),
    by = c("sample", "locus", "field", "genotyper")
  )%>% 
  left_join(subsample_cells_alignment_stats_df, by = c("sample", "locus")) %>% 
  left_join(genome_region_reference, by = "locus") %>%
  left_join(subsample_cell_counts_df, by = "sample") %>%
  bind_rows(accuracy_df) %>% 
  mutate_at(vars(c(reads, bp)), as.numeric) %>% 
  mutate(coverage = (reads * 91)/bp) %>% 
  mutate(n_alleles = map_dbl(allele, function(x) sum(!is.na(x)))) %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)", sample) & genotyper != "hlaminer") 
# saveRDS(subsample_cells_combined_df, "subsample_cells_combined_df.RDS")

Coverage plots

gg_coverage(subsample_cells_combined_df, x_var = "coverage", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

gg_coverage(subsample_cells_combined_df, x_var = "coverage", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

gg_coverage(subsample_cells_combined_df, x_var = "coverage", y_var = "n_alleles", facet_genotyper = T)

Cell number plots

gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

plt_cell_accuracy_all <- gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "accuracy") 
plt_cell_accuracy_abc <- gg_coverage(subsample_cells_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "n_cells", y_var = "accuracy") 
gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "success") 
`geom_smooth()` using formula 'y ~ x'

plt_cell_success_all <- gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "success") 
plt_cell_success_abc <- gg_coverage(subsample_cells_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "n_cells", y_var = "success") 
gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)

plt_cell_allele_all <- gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)
plt_cell_allele_abc <- gg_coverage(subsample_cells_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)
# subsample_cells_combined_df %>% 
#   mutate(reads = reads/n_cells) %>% 
#   filter(reads < 100) %>% 
#   gg_coverage(x_var = "reads", y_var = "accuracy", x_log = F) 

Combined plots

no_leg <- list(theme(legend.position = "none"))
legend <- cowplot::get_legend(plt_read_accuracy_abc)
`geom_smooth()` using formula 'y ~ x'
plot_grid(
  plot_grid(
    plt_read_accuracy_abc + no_leg,
    plt_read_success_abc + no_leg,
    plt_read_allele_abc,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[1:3]
  ),
  plot_grid(
    plt_cell_accuracy_abc + no_leg,
    plt_cell_success_abc + no_leg,
    plt_cell_allele_abc,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[4:6]
  ),
  plot_grid(
    legend,
    legend,
    NA,
    ncol = 1
  ),
  nrow = 1,
  rel_widths = c(1,1,0.25)
)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

no_leg <- list(theme(legend.position = "none"))
legend <- cowplot::get_legend(plt_read_accuracy_all)
`geom_smooth()` using formula 'y ~ x'
plot_grid(
  plot_grid(
    plt_read_accuracy_all + no_leg,
    plt_read_success_all + no_leg,
    plt_read_allele_all,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[1:3]
  ),
  plot_grid(
    plt_cell_accuracy_all + no_leg,
    plt_cell_success_all + no_leg,
    plt_cell_allele_all,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[4:6]
  ),
  plot_grid(
    legend,
    legend,
    NA,
    ncol = 1
  ),
  nrow = 1,
  rel_widths = c(1,1,0.25)
)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

Main plot

loci <- c("A", "B", "C", "DPB1", "DQA1", "DRB1")
plt_read_accuracy_subset <- gg_coverage(
  subsample_reads_combined_df %>% 
    filter(locus %in% loci) %>% 
    filter(!(genotyper == "phlat" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DQA1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DRB1")), 
  x_var = "reads", y_var = "accuracy") +
  facet_wrap(~locus, nrow = 2)
plt_read_accuracy_subset
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 59 rows containing non-finite values (stat_smooth).
Warning: Removed 59 rows containing missing values (geom_point).

save_plot(here("figures_pdf/v2/5_coverage.pdf"), plt_read_accuracy_subset, base_height = 4, base_width = 6)
plt_read_allele_all <- gg_coverage(subsample_reads_combined_df %>% 
    filter(locus %in% loci) %>% 
    filter(!(genotyper == "phlat" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DQA1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DRB1")), x_var = "reads", y_var = "n_alleles", facet_genotyper = T)
plt_read_allele_all
Warning: Removed 62 rows containing missing values (geom_point).

plt_main <- plot_grid(
  plt_read_accuracy_subset,
  plt_read_allele_all,
  ncol = 1,
  rel_heights = c(3,2),
  align = "v",
  axis = "lr",
  labels = c("A", "B")
)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 59 rows containing non-finite values (stat_smooth).
Warning: Removed 59 rows containing missing values (geom_point).
Warning: Removed 62 rows containing missing values (geom_point).
plt_main

save_plot(here("figures_pdf/v2/5_coverage.pdf"), plt_main, base_height = 7, base_width = 7)

Slope stats

get_lm_stats <- function(df, x_var = "coverage", y_var = "accuracy", field_var = "field_2", x_log = T){
  # Input checks
  arg_col <- makeAssertCollection()
  assertChoice(y_var, c("accuracy", "success", "n_alleles"), add = arg_col)
  assertChoice(x_var, c("reads", "coverage", "n_cells"), add = arg_col)
  assertChoice(field_var, c("field_1", "field_2", "field_3"), add = arg_col)
  assertLogical(x_log, add = arg_col)
  if (arg_col$isEmpty()==F) {map(arg_col$getMessages(),print);reportAssertions(arg_col)}
  
  # browser()
  df <- df %>% 
    filter(field == field_var) %>% 
    exclude_genotyper_fields() # Replace w/ exclude_genotyper_fields?
  if (x_log ==T){df <- df %>% mutate_at(x_var, log10)}
  df %>%
  filter_at(x_var, function(x) !is.na(x) & !is.infinite(x)) %>% 
  group_by(locus, field, genotyper) %>%
  nest() %>%
  mutate(data = map(data, function(x){
    summary(lm(!!sym(y_var) ~ !!sym(x_var), data = x)) %>% broom::tidy() %>% filter(term == x_var)
  })) %>% unnest_wider(data) %>%
  mutate(est_min = round(estimate - 2*std.error, 2),
         est_max = round(estimate + 2*std.error, 2))
}

model_stats <- get_lm_stats(subsample_cells_combined_df, x_var = "reads", y_var = "accuracy", x_log = T)
model_stats
model_stats <- bind_rows(
  get_lm_stats(subsample_reads_combined_df, x_var = "reads", y_var = "accuracy", x_log = T) %>% mutate(x_var = "reads", y_var = "accuracy"),
  get_lm_stats(subsample_reads_combined_df, x_var = "reads", y_var = "success", x_log = T) %>% mutate(x_var = "reads", y_var = "success"),
  get_lm_stats(subsample_cells_combined_df, x_var = "n_cells", y_var = "accuracy", x_log = T) %>% mutate(x_var = "cells", y_var = "accuracy"),
  get_lm_stats(subsample_cells_combined_df, x_var = "n_cells", y_var = "success", x_log = T) %>% mutate(x_var = "cells", y_var = "success")
)  %>% 
  # filter(grepl("^[ABC]", locus)) %>% 
  ungroup() %>% 
  filter(field == "field_2") 

Table

Scratch work

LS0tCnRpdGxlOiAiNSkgQ292ZXJhZ2UgQW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KCgojIFN0YXJ0dXAgCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ2dwbWlzYykKbGlicmFyeShoZXJlKQoKc291cmNlKGhlcmUoImhlbHBlcl9mdW5jdGlvbnMvZGF0YV9pbXBvcnRfZnVuY3Rpb25zLlIiKSkKc291cmNlKGhlcmUoImhlbHBlcl9mdW5jdGlvbnMvZmlndXJlX2Zvcm1hdF9mdW5jdGlvbnMuUiIpKQpzb3VyY2UoaGVyZSgiaGVscGVyX2Z1bmN0aW9ucy9jYWxjdWxhdGlvbl9mdW5jdGlvbnMuUiIpKQppc2JfcGF0aCA8LSBoZXJlKCJkYXRhL2lzYiIpCmlzYl9zYW1wbGVzIDwtIHJlYWRfdHN2KGhlcmUoImRhdGEvaXNiL2xvZ3MvcGFyYWxsZWwubG9nIikpICU+JSAKICBzZXBhcmF0ZShDb21tYW5kLCBpbnRvID0gYyhOQSwgInNhbXBsZSIpLCBzZXAgPSAiICIpICU+JSAKICBwdWxsKHNhbXBsZSkgJT4lIHVuaXF1ZSgpCmBgYAoKIyMjIEdldCByZWZlcmVuY2UgYWxsZWxlcwoKLSBHQ2gzOCByZWZlcmVuY2UgYWxsZWxlcyAocGVyIGh0dHBzOi8vd3d3LmViaS5hYy51ay9pcGQvaW1ndC9obGEvaGVscC9nZW5vbWljcy5odG1sKQotIFRvIGJlIHVzZWQgdG8gY2FsY3VsYXRlIGNvdmVyYWdlCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CnJlZmVyZW5jZV9hbGxlbGVzIDwtIHJlYWRfdHN2KGhlcmUoInJlZmVyZW5jZXMvR0NoMzhfcmVmZXJlbmNlX2FsbGVsZXMudHh0IiksIGNvbF9uYW1lcyA9IEYpICU+JSBwdWxsKDEpCmdlbm9tZV9yZWdpb25fcmVmZXJlbmNlIDwtIGdldF9hbGxlbGVfbGVuZ3RoKHJlZmVyZW5jZV9hbGxlbGVzKQpgYGAKCiMjIyBHZXQgYXJjYXNITEEgYWxpZ25tZW50IHN0YXRzCgpgYGB7cn0KYXJjYXNfbG9nX2RpciA8LSBzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIGlzYl9wYXRoKQphbGlnbm1lbnRfc3RhdHNfZGYgPC0gaGxhX21hcHBpbmdfc3RhdHNfaW1wb3J0KGlzYl9zYW1wbGVzLCBhcmNhc19sb2dfZGlyKQpgYGAKCiMjIyBJbXBvcnQgY2VsbCBjb3VudHMKCmBgYHtyfQpjZWxsX2NvdW50c19kZiA8LSByZWFkUkRTKGhlcmUoImRhdGEvaXNiL2lzYl9zZXF1ZW5jZWRfY2VsbF9jb3VudC5SRFMiKSkKYGBgCgojIyMgQXNzZW1ibGUgYWNjdXJhY3kgREYKCmBgYHtyfQpzdWNjZXNzX2RmIDwtIHJlYWRSRFMoaGVyZSgiMl9hY2N1cmFjeS9pc2Jfc3VjY2Vzcy5SRFMiKSkKYWNjdXJhY3lfZGYgPC0gcmVhZFJEUyhoZXJlKCIzX0RSQi9pc2JfYWNjdXJhY3lfZHJiMzQ1X2ZpbHRlcmVkLlJEUyIpKQphY2N1cmFjeV9kZiA8LSBhY2N1cmFjeV9kZiAlPiUgCiAgbGVmdF9qb2luKAogICAgc3VjY2Vzc19kZiAlPiUgc2VsZWN0KHNhbXBsZTpmaWVsZCwgZ2Vub3R5cGVyLCBzdWNjZXNzID0gYWNjdXJhY3kpLAogICAgYnkgPSBjKCJzYW1wbGUiLCAibG9jdXMiLCAiZmllbGQiLCAiZ2Vub3R5cGVyIikKICApJT4lIAogIGxlZnRfam9pbihhbGlnbm1lbnRfc3RhdHNfZGYsIGJ5ID0gYygic2FtcGxlIiwgImxvY3VzIikpICU+JSAKICBsZWZ0X2pvaW4oZ2Vub21lX3JlZ2lvbl9yZWZlcmVuY2UsIGJ5ID0gImxvY3VzIikgJT4lCiAgbGVmdF9qb2luKGNlbGxfY291bnRzX2RmLCBieSA9ICJzYW1wbGUiKSAlPiUKICBtdXRhdGVfYXQodmFycyhjKHJlYWRzLCBicCkpLCBhcy5udW1lcmljKSAlPiUgCiAgbXV0YXRlKGNvdmVyYWdlID0gKHJlYWRzICogOTEpL2JwKSAlPiUgCiAgbXV0YXRlKG5fYWxsZWxlcyA9IG1hcF9kYmwoYWxsZWxlLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5bQUJDXXxELltBQl0xIiwgbG9jdXMpICYgZ3JlcGwoIi0oQUN8QkwpJCIsIHNhbXBsZSkgJiBnZW5vdHlwZXIgIT0gImhsYW1pbmVyIikgCmBgYAoKCiMjIyBDb3ZlcmFnZSBiYXNlZCBwbG90cwoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0yLjUsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2UoYWNjdXJhY3lfZGYsIHhfdmFyID0gImNvdmVyYWdlIiwgeV92YXIgPSAiYWNjdXJhY3kiKSAKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKGFjY3VyYWN5X2RmLCB4X3ZhciA9ICJjb3ZlcmFnZSIsIHlfdmFyID0gInN1Y2Nlc3MiKQpgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKGFjY3VyYWN5X2RmLCB4X3ZhciA9ICJjb3ZlcmFnZSIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCmBgYAoKIyMjIENlbGwgbnVtYmVyIGJhc2VkIHBsb3RzCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShhY2N1cmFjeV9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gImFjY3VyYWN5IikgCmBgYApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShhY2N1cmFjeV9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gInN1Y2Nlc3MiKQpgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKGFjY3VyYWN5X2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAibl9hbGxlbGVzIiwgZmFjZXRfZ2Vub3R5cGVyID0gVCkKYGBgCgoKIyBTdWJzYW1wbGUgUkVBRFMKCiMjIyBJbXBvcnQgcmVhZCBzdWJzYW1wbGUgZGF0YQoKYGBge3J9CiMgSW1wb3J0IHByZWRpY3RlZCBnZW5vdHlwZXMKc3Vic2FtcGxlX3JlYWRzX3BhdGggPC0gaGVyZSgiZGF0YS9pc2Ivc3Vic2FtcGxlIikKc3Vic2FtcGxlX3JlYWRzX3NhbXBsZXMgPC0gdGliYmxlKHNhbXBsZSA9IGxpc3QuZmlsZXMoc3ByaW50ZigiJXMvYXJjYXNITEEiLCBzdWJzYW1wbGVfcmVhZHNfcGF0aCkpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJeSU5DT1YiLCBzYW1wbGUpKSAlPiUgCiAgc2VwYXJhdGUoc2FtcGxlLCBpbnRvID0gYygic2FtcGxlIiksIHNlcCA9ICJcXC4iLCBleHRyYSA9ICJkcm9wIikgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHB1bGwoc2FtcGxlKQpzdWJzYW1wbGVfcmVhZHNfZGYgPC0gZm9ybWF0X2hsYV90YWJsZShjb21iaW5lX0hMQV9pbXBvcnQocGF0aCA9IHN1YnNhbXBsZV9yZWFkc19wYXRoLCBzYW1wbGVzID0gc3Vic2FtcGxlX3JlYWRzX3NhbXBsZXMpKQpzdWJzYW1wbGVfcmVhZHNfZGYgPC0gc3Vic2FtcGxlX3JlYWRzX2RmICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5bQUJDXXxELltBQl0xIiwgbG9jdXMpICYgZ3JlcGwoIi0oQUN8QkwpXyIsIHNhbXBsZSkpCnN1YnNhbXBsZV9yZWFkc19kZgpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRn0KIyBFeHBhbmQgaW52aXRybyBnZW5vdHlwZXMgYWNyb3NzIHJlYWQgc3Vic2FtcGxlIG5hbWVzCmludml0cm9fZGYgPC0gaW52aXRyb19pbXBvcnQoKQppbnZpdHJvX3JlYWRzX2RmIDwtIHN1YnNhbXBsZV9yZWFkc19kZiAlPiUgCiAgc2VsZWN0KHNhbXBsZSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHNlcGFyYXRlKHNhbXBsZSwgaW50byA9IGMoInNhbXBsZSIsICJzdWJzYW1wbGUiKSwgc2VwID0gIl8iKSAlPiUgCiAgbGVmdF9qb2luKGludml0cm9fZGYsIGJ5ID0gInNhbXBsZSIpICU+JSAKICB1bml0ZShzYW1wbGUsIHNhbXBsZSwgc3Vic2FtcGxlLCBzZXAgPSAiXyIpICU+JSAKICBmb3JtYXRfaGxhX3RhYmxlKCkgJT4lIAogIGRyb3BfbmEoKQppbnZpdHJvX3JlYWRzX2RmCmBgYAoKYGBge3J9CiMgSW1wb3J0IHJlYWQgc3RhdGlzdGljcwpzdWJzYW1wbGVfcmVhZHNfYXJjYXNfbG9nX2RpciA8LSBzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIHN1YnNhbXBsZV9yZWFkc19wYXRoKQpzdWJzYW1wbGVfcmVhZHNfYWxpZ25tZW50X3N0YXRzX2RmIDwtIGhsYV9tYXBwaW5nX3N0YXRzX2ltcG9ydChzdWJzYW1wbGVfcmVhZHNfc2FtcGxlcywgc3Vic2FtcGxlX3JlYWRzX2FyY2FzX2xvZ19kaXIpCmBgYAoKIyMjIENhbGN1bGF0ZSBhY2N1cmFjeQoKYGBge3J9CiMgQ2FsY3VsYXRlIGFjY3VyYWN5CnN1YnNhbXBsZV9yZWFkc19hY2N1cmFjeV9kZiA8LSBjb21wYXJlX2hsYSgKICBobGFfZGYgPSBiaW5kX3Jvd3MoCiAgICBzdWJzYW1wbGVfcmVhZHNfZGYsCiAgICBpbnZpdHJvX3JlYWRzX2RmCiAgKSwKICByZWZlcmVuY2UgPSAiaW52aXRybyIsIAogIG1ldGhvZCA9ICJhY2N1cmFjeSIKKQojIHNhdmVSRFMoc3Vic2FtcGxlX3JlYWRzX2FjY3VyYWN5X2RmLCBoZXJlKCI1X2NvdmVyYWdlX2FjY3VyYWN5L2lzYl9zdWJzYW1wbGVfcmVhZHNfYWNjdXJhY3kuUkRTIikpCmBgYAoKCmBgYHtyfQojIENhbGN1bGF0ZSBzdWNjZXNzCnN1YnNhbXBsZV9yZWFkc19zdWNjZXNzX2RmIDwtIGNvbXBhcmVfaGxhKAogIGhsYV9kZiA9IGJpbmRfcm93cygKICAgIHN1YnNhbXBsZV9yZWFkc19kZiwKICAgIGludml0cm9fcmVhZHNfZGYKICApLAogIHJlZmVyZW5jZSA9ICJpbnZpdHJvIiwgCiAgbWV0aG9kID0gInN1Y2Nlc3MiCikKIyBzYXZlUkRTKHN1YnNhbXBsZV9yZWFkc19zdWNjZXNzX2RmLCBoZXJlKCI1X2NvdmVyYWdlX2FjY3VyYWN5L2lzYl9zdWJzYW1wbGVfcmVhZHNfc3VjY2Vzcy5SRFMiKSkKYGBgCgpgYGB7cn0KIyBDb21iaW5lIHZhcmlvdXMgZGF0YQpzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYgPC0gc3Vic2FtcGxlX3JlYWRzX2FjY3VyYWN5X2RmICU+JSAKICBsZWZ0X2pvaW4oCiAgICBzdWJzYW1wbGVfcmVhZHNfc3VjY2Vzc19kZiAlPiUgc2VsZWN0KHNhbXBsZTpmaWVsZCwgZ2Vub3R5cGVyLCBzdWNjZXNzID0gYWNjdXJhY3kpLAogICAgYnkgPSBjKCJzYW1wbGUiLCAibG9jdXMiLCAiZmllbGQiLCAiZ2Vub3R5cGVyIikKICApJT4lIAogIGxlZnRfam9pbihzdWJzYW1wbGVfcmVhZHNfYWxpZ25tZW50X3N0YXRzX2RmLCBieSA9IGMoInNhbXBsZSIsICJsb2N1cyIpKSAlPiUgCiAgbGVmdF9qb2luKGdlbm9tZV9yZWdpb25fcmVmZXJlbmNlLCBieSA9ICJsb2N1cyIpICU+JQogIGJpbmRfcm93cyhhY2N1cmFjeV9kZiAlPiUgc2VsZWN0KC1uX2NlbGxzKSkgJT4lICMgTm8gY2VsbCBudW1iZXJzIGZvciByZWFkIHN1YnNhbXBsZXMKICBtdXRhdGVfYXQodmFycyhjKHJlYWRzLCBicCkpLCBhcy5udW1lcmljKSAlPiUgCiAgbXV0YXRlKGNvdmVyYWdlID0gKHJlYWRzICogOTEpL2JwKSAlPiUgICMgQ2FsY3VsYXRlIGNvdmVyYWdlCiAgbXV0YXRlKG5fYWxsZWxlcyA9IG1hcF9kYmwoYWxsZWxlLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkpICU+JSAgIyBDYWxjdWxhdGUgbl9hbGxlbGVzCiAgZmlsdGVyKGdyZXBsKCJeW0FCQ118RC5bQUJdMSIsIGxvY3VzKSAmIGdyZXBsKCItKEFDfEJMKSIsIHNhbXBsZSkgJiBnZW5vdHlwZXIgIT0gImhsYW1pbmVyIikgCiMgc2F2ZVJEUyhzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIGhlcmUoIjVfY292ZXJhZ2VfYWNjdXJhY3kvc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmLlJEUyIpKQojIHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiA8LSByZWFkUkRTKGhlcmUoIjVfY292ZXJhZ2VfYWNjdXJhY3kvc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmLlJEUyIpKQpgYGAKCiMjIyBSZWFkcyBwbG90cwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiKSAKcGx0X3JlYWRfYWNjdXJhY3lfYWxsIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpIApwbHRfcmVhZF9hY2N1cmFjeV9hYmMgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmICU+JSBmaWx0ZXIoZ3JlcGwoIl5bQUJDXSIsIGxvY3VzKSksIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiKSAKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJzdWNjZXNzIikKcGx0X3JlYWRfc3VjY2Vzc19hbGwgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gInN1Y2Nlc3MiKQpwbHRfcmVhZF9zdWNjZXNzX2FiYyA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYgJT4lIGZpbHRlcihncmVwbCgiXltBQkNdIiwgbG9jdXMpKSwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJzdWNjZXNzIikgCmBgYApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCnBsdF9yZWFkX2FsbGVsZV9hbGwgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCnBsdF9yZWFkX2FsbGVsZV9hYmMgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmICU+JSBmaWx0ZXIoZ3JlcGwoIl5bQUJDXSIsIGxvY3VzKSksIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAibl9hbGxlbGVzIiwgZmFjZXRfZ2Vub3R5cGVyID0gVCkKYGBgCi0gRGVmYXVsdCBhcmNhc0hMQSBjdXQgb2ZmIDc1IHJlYWRzIChsb2cxMCA9IDEuOCkKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYgJT4lIAogIGZpbHRlcihmaWVsZCA9PSAiZmllbGRfMiIpICU+JSAKICBtdXRhdGUobl9hbGxlbGVzID0gZmFjdG9yKG5fYWxsZWxlcywgbGV2ZWxzID0gMDoyKSkgJT4lIAogIGdncGxvdChhZXMoeSA9IG5fYWxsZWxlcywgeCA9IGxvZzEwKGNvdmVyYWdlKSkpKwogICMgZ2VvbV92aW9saW4oc2NhbGUgPSAiY291bnQiLCBhbHBoYSA9IDAuNSwgKSsKICBnZW9tX2ppdHRlcihzaXplID0gMC4yLCB3aWR0aCA9IDAsIGhlaWdodCA9MC4yLCBhbHBoYSA9IDAuMikrCiAgIyBnZW9tX2JveHBsb3Qod2lkdGg9MC4xKSsKICAjIGdnYmVlc3dhcm06Omdlb21fYmVlc3dhcm0oc2l6ZSA9IDAuMSwgYWxwaGEgPSAwLjEpKwogICMgY29vcmRfZmxpcCgpKwogIGZhY2V0X2dyaWQoZ2Vub3R5cGVyIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIikrCiAgdGhlbWVfYncoKQpgYGAKCiMgU3Vic2FtcGxlIENFTExTCgpgYGB7cn0KIyBJbXBvcnQgcHJlZGljdGVkIGdlbm90eXBlcwpzdWJzYW1wbGVfY2VsbHNfcGF0aCA8LSBoZXJlKCJkYXRhL2lzYi9zdWJzYW1wbGVfY2VsbHMiKQpzdWJzYW1wbGVfY2VsbHNfc2FtcGxlcyA8LSB0aWJibGUoc2FtcGxlID0gbGlzdC5maWxlcyhzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIHN1YnNhbXBsZV9jZWxsc19wYXRoKSkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5JTkNPViIsIHNhbXBsZSkpICU+JSAKICBzZXBhcmF0ZShzYW1wbGUsIGludG8gPSBjKCJzYW1wbGUiKSwgc2VwID0gIlxcLiIsIGV4dHJhID0gImRyb3AiKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUgCiAgcHVsbChzYW1wbGUpCnN1YnNhbXBsZV9jZWxsc19kZiA8LSBmb3JtYXRfaGxhX3RhYmxlKGNvbWJpbmVfSExBX2ltcG9ydChwYXRoID0gc3Vic2FtcGxlX2NlbGxzX3BhdGgsIHNhbXBsZXMgPSBzdWJzYW1wbGVfY2VsbHNfc2FtcGxlcykpCnN1YnNhbXBsZV9jZWxsc19kZiA8LSBzdWJzYW1wbGVfY2VsbHNfZGYgJT4lIAogIGZpbHRlcihncmVwbCgiXltBQkNdfEQuW0FCXTEiLCBsb2N1cykgJiBncmVwbCgiLShBQ3xCTClfIiwgc2FtcGxlKSkKc3Vic2FtcGxlX2NlbGxzX2RmCmBgYAoKCmBgYHtyLCBtZXNzYWdlID0gRn0KIyBFeHBhbmQgaW52aXRybyBnZW5vdHlwZXMgYWNyb3NzIHJlYWQgc3Vic2FtcGxlIG5hbWVzCmludml0cm9fZGYgPC0gaW52aXRyb19pbXBvcnQoKQppbnZpdHJvX2NlbGxzX2RmIDwtIHN1YnNhbXBsZV9jZWxsc19kZiAlPiUgCiAgc2VsZWN0KHNhbXBsZSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHNlcGFyYXRlKHNhbXBsZSwgaW50byA9IGMoInNhbXBsZSIsICJzdWJzYW1wbGUiKSwgc2VwID0gIl8iKSAlPiUgCiAgbGVmdF9qb2luKGludml0cm9fZGYsIGJ5ID0gInNhbXBsZSIpICU+JSAKICB1bml0ZShzYW1wbGUsIHNhbXBsZSwgc3Vic2FtcGxlLCBzZXAgPSAiXyIpICU+JSAKICBmb3JtYXRfaGxhX3RhYmxlKCkgJT4lIAogIGRyb3BfbmEoKQppbnZpdHJvX2NlbGxzX2RmCmBgYAoKYGBge3J9CiMgSW1wb3J0IHJlYWQgc3RhdGlzdGljcwpzdWJzYW1wbGVfY2VsbHNfYXJjYXNfbG9nX2RpciA8LSBzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIHN1YnNhbXBsZV9jZWxsc19wYXRoKQpzdWJzYW1wbGVfY2VsbHNfYWxpZ25tZW50X3N0YXRzX2RmIDwtIGhsYV9tYXBwaW5nX3N0YXRzX2ltcG9ydChzdWJzYW1wbGVfY2VsbHNfc2FtcGxlcywgc3Vic2FtcGxlX2NlbGxzX2FyY2FzX2xvZ19kaXIpCmBgYAoKYGBge3J9CiMgSW1wb3J0IGNlbGwgY291bnRzIGZvciBlYWNoIHN1YnNhbXBsZQpzdWJzYW1wbGVfY2VsbF9jb3VudHNfZGYgPC0gcmVhZF9jc3YoaGVyZSgiZGF0YS9pc2Ivc3Vic2FtcGxlX2NlbGxzL3N1YnNhbXBsZV9jZWxsX2NvdW50cy5jc3YiKSkKYGBgCgojIyMgQ2FsY3VsYXRlIGFjY3VyYWN5CgpgYGB7cn0KIyBDYWxjdWxhdGUgYWNjdXJhY3kKc3Vic2FtcGxlX2NlbGxzX2FjY3VyYWN5X2RmIDwtIGNvbXBhcmVfaGxhKAogIGhsYV9kZiA9IGJpbmRfcm93cygKICAgIHN1YnNhbXBsZV9jZWxsc19kZiwKICAgIGludml0cm9fY2VsbHNfZGYKICApLAogIHJlZmVyZW5jZSA9ICJpbnZpdHJvIiwgCiAgbWV0aG9kID0gImFjY3VyYWN5IgopCiMgc2F2ZVJEUyhzdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3lfZGYsIGhlcmUoIjVfY292ZXJhZ2VfYWNjdXJhY3kvaXNiX3N1YnNhbXBsZV9jZWxsc19hY2N1cmFjeS5SRFMiKSkKIyBzdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3lfZGYgPC0gcmVhZFJEUyhoZXJlKCI1X2NvdmVyYWdlX2FjY3VyYWN5L2lzYl9zdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3kuUkRTIikpCmBgYAoKYGBge3J9CiMgQ2FsY3VsYXRlIHN1Y2Nlc3MKc3Vic2FtcGxlX2NlbGxzX3N1Y2Nlc3NfZGYgPC0gY29tcGFyZV9obGEoCiAgaGxhX2RmID0gYmluZF9yb3dzKAogICAgc3Vic2FtcGxlX2NlbGxzX2RmLAogICAgaW52aXRyb19jZWxsc19kZgogICksCiAgcmVmZXJlbmNlID0gImludml0cm8iLCAKICBtZXRob2QgPSAic3VjY2VzcyIKKQojIHNhdmVSRFMoc3Vic2FtcGxlX2NlbGxzX3N1Y2Nlc3NfZGYsIGhlcmUoIjVfY292ZXJhZ2VfYWNjdXJhY3kvaXNiX3N1YnNhbXBsZV9jZWxsc19zdWNjZXNzLlJEUyIpKQojIHN1YnNhbXBsZV9jZWxsc19zdWNjZXNzX2RmIDwtIHJlYWRSRFMoaGVyZSgiNV9jb3ZlcmFnZV9hY2N1cmFjeS9pc2Jfc3Vic2FtcGxlX2NlbGxzX3N1Y2Nlc3MuUkRTIikpCmBgYAoKYGBge3J9CiMgQ29tYmluZSB2YXJpb3VzIGRhdGEKc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmIDwtIHN1YnNhbXBsZV9jZWxsc19hY2N1cmFjeV9kZiAlPiUgCiAgbGVmdF9qb2luKAogICAgc3Vic2FtcGxlX2NlbGxzX3N1Y2Nlc3NfZGYgJT4lIHNlbGVjdChzYW1wbGU6ZmllbGQsIGdlbm90eXBlciwgc3VjY2VzcyA9IGFjY3VyYWN5KSwKICAgIGJ5ID0gYygic2FtcGxlIiwgImxvY3VzIiwgImZpZWxkIiwgImdlbm90eXBlciIpCiAgKSU+JSAKICBsZWZ0X2pvaW4oc3Vic2FtcGxlX2NlbGxzX2FsaWdubWVudF9zdGF0c19kZiwgYnkgPSBjKCJzYW1wbGUiLCAibG9jdXMiKSkgJT4lIAogIGxlZnRfam9pbihnZW5vbWVfcmVnaW9uX3JlZmVyZW5jZSwgYnkgPSAibG9jdXMiKSAlPiUKICBsZWZ0X2pvaW4oc3Vic2FtcGxlX2NlbGxfY291bnRzX2RmLCBieSA9ICJzYW1wbGUiKSAlPiUKICBiaW5kX3Jvd3MoYWNjdXJhY3lfZGYpICU+JSAKICBtdXRhdGVfYXQodmFycyhjKHJlYWRzLCBicCkpLCBhcy5udW1lcmljKSAlPiUgCiAgbXV0YXRlKGNvdmVyYWdlID0gKHJlYWRzICogOTEpL2JwKSAlPiUgCiAgbXV0YXRlKG5fYWxsZWxlcyA9IG1hcF9kYmwoYWxsZWxlLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKSkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5bQUJDXXxELltBQl0xIiwgbG9jdXMpICYgZ3JlcGwoIi0oQUN8QkwpIiwgc2FtcGxlKSAmIGdlbm90eXBlciAhPSAiaGxhbWluZXIiKSAKIyBzYXZlUkRTKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgaGVyZSgiNV9jb3ZlcmFnZV9hY2N1cmFjeS9zdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYuUkRTIikpCmBgYAoKIyMjIENvdmVyYWdlIHBsb3RzCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gImNvdmVyYWdlIiwgeV92YXIgPSAiYWNjdXJhY3kiKSAKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAiY292ZXJhZ2UiLCB5X3ZhciA9ICJzdWNjZXNzIikKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gImNvdmVyYWdlIiwgeV92YXIgPSAibl9hbGxlbGVzIiwgZmFjZXRfZ2Vub3R5cGVyID0gVCkKYGBgCgojIyMgQ2VsbCBudW1iZXIgcGxvdHMKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gImFjY3VyYWN5IikgCnBsdF9jZWxsX2FjY3VyYWN5X2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpIApwbHRfY2VsbF9hY2N1cmFjeV9hYmMgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSBmaWx0ZXIoZ3JlcGwoIl5bQUJDXSIsIGxvY3VzKSksIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpIApgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0yLjUsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAic3VjY2VzcyIpIApwbHRfY2VsbF9zdWNjZXNzX2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJzdWNjZXNzIikgCnBsdF9jZWxsX3N1Y2Nlc3NfYWJjIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiAlPiUgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAic3VjY2VzcyIpIApgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCnBsdF9jZWxsX2FsbGVsZV9hbGwgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAibl9hbGxlbGVzIiwgZmFjZXRfZ2Vub3R5cGVyID0gVCkKcGx0X2NlbGxfYWxsZWxlX2FiYyA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYgJT4lIGZpbHRlcihncmVwbCgiXltBQkNdIiwgbG9jdXMpKSwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQojIHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiAlPiUgCiMgICBtdXRhdGUocmVhZHMgPSByZWFkcy9uX2NlbGxzKSAlPiUgCiMgICBmaWx0ZXIocmVhZHMgPCAxMDApICU+JSAKIyAgIGdnX2NvdmVyYWdlKHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiLCB4X2xvZyA9IEYpIApgYGAKCgoKCgoKCgojIENvbWJpbmVkIHBsb3RzCmBgYHtyLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNywgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGfQpub19sZWcgPC0gbGlzdCh0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKQpsZWdlbmQgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfcmVhZF9hY2N1cmFjeV9hYmMpCnBsb3RfZ3JpZCgKICBwbG90X2dyaWQoCiAgICBwbHRfcmVhZF9hY2N1cmFjeV9hYmMgKyBub19sZWcsCiAgICBwbHRfcmVhZF9zdWNjZXNzX2FiYyArIG5vX2xlZywKICAgIHBsdF9yZWFkX2FsbGVsZV9hYmMsCiAgICBuY29sID0gMSwKICAgIGFsaWduID0gInYiLAogICAgYXhpcyA9ICJsciIsCiAgICByZWxfaGVpZ2h0cyA9IGMoMSwxLDEuMiksCiAgICBsYWJlbHMgPSBMRVRURVJTWzE6M10KICApLAogIHBsb3RfZ3JpZCgKICAgIHBsdF9jZWxsX2FjY3VyYWN5X2FiYyArIG5vX2xlZywKICAgIHBsdF9jZWxsX3N1Y2Nlc3NfYWJjICsgbm9fbGVnLAogICAgcGx0X2NlbGxfYWxsZWxlX2FiYywKICAgIG5jb2wgPSAxLAogICAgYWxpZ24gPSAidiIsCiAgICBheGlzID0gImxyIiwKICAgIHJlbF9oZWlnaHRzID0gYygxLDEsMS4yKSwKICAgIGxhYmVscyA9IExFVFRFUlNbNDo2XQogICksCiAgcGxvdF9ncmlkKAogICAgbGVnZW5kLAogICAgbGVnZW5kLAogICAgTkEsCiAgICBuY29sID0gMQogICksCiAgbnJvdyA9IDEsCiAgcmVsX3dpZHRocyA9IGMoMSwxLDAuMjUpCikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSA3LCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9Cm5vX2xlZyA8LSBsaXN0KHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpCmxlZ2VuZCA8LSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9yZWFkX2FjY3VyYWN5X2FsbCkKcGxvdF9ncmlkKAogIHBsb3RfZ3JpZCgKICAgIHBsdF9yZWFkX2FjY3VyYWN5X2FsbCArIG5vX2xlZywKICAgIHBsdF9yZWFkX3N1Y2Nlc3NfYWxsICsgbm9fbGVnLAogICAgcGx0X3JlYWRfYWxsZWxlX2FsbCwKICAgIG5jb2wgPSAxLAogICAgYWxpZ24gPSAidiIsCiAgICBheGlzID0gImxyIiwKICAgIHJlbF9oZWlnaHRzID0gYygxLDEsMS4yKSwKICAgIGxhYmVscyA9IExFVFRFUlNbMTozXQogICksCiAgcGxvdF9ncmlkKAogICAgcGx0X2NlbGxfYWNjdXJhY3lfYWxsICsgbm9fbGVnLAogICAgcGx0X2NlbGxfc3VjY2Vzc19hbGwgKyBub19sZWcsCiAgICBwbHRfY2VsbF9hbGxlbGVfYWxsLAogICAgbmNvbCA9IDEsCiAgICBhbGlnbiA9ICJ2IiwKICAgIGF4aXMgPSAibHIiLAogICAgcmVsX2hlaWdodHMgPSBjKDEsMSwxLjIpLAogICAgbGFiZWxzID0gTEVUVEVSU1s0OjZdCiAgKSwKICBwbG90X2dyaWQoCiAgICBsZWdlbmQsCiAgICBsZWdlbmQsCiAgICBOQSwKICAgIG5jb2wgPSAxCiAgKSwKICBucm93ID0gMSwKICByZWxfd2lkdGhzID0gYygxLDEsMC4yNSkKKQpgYGAKCiMjIyBNYWluIHBsb3QKCmBgYHtyfQpsb2NpIDwtIGMoIkEiLCAiQiIsICJDIiwgIkRQQjEiLCAiRFFBMSIsICJEUkIxIikKcGx0X3JlYWRfYWNjdXJhY3lfc3Vic2V0IDwtIGdnX2NvdmVyYWdlKAogIHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiAlPiUgCiAgICBmaWx0ZXIobG9jdXMgJWluJSBsb2NpKSAlPiUgCiAgICBmaWx0ZXIoIShnZW5vdHlwZXIgPT0gInBobGF0IiAmIGxvY3VzID09ICJEUEIxIikpICU+JSAKICAgIGZpbHRlcighKGdlbm90eXBlciA9PSAib3B0aXR5cGUiICYgbG9jdXMgPT0gIkRQQjEiKSkgJT4lIAogICAgZmlsdGVyKCEoZ2Vub3R5cGVyID09ICJvcHRpdHlwZSIgJiBsb2N1cyA9PSAiRFFBMSIpKSAlPiUgCiAgICBmaWx0ZXIoIShnZW5vdHlwZXIgPT0gIm9wdGl0eXBlIiAmIGxvY3VzID09ICJEUkIxIikpLCAKICB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gImFjY3VyYWN5IikgKwogIGZhY2V0X3dyYXAofmxvY3VzLCBucm93ID0gMikKcGx0X3JlYWRfYWNjdXJhY3lfc3Vic2V0CmBgYApgYGB7cn0Kc2F2ZV9wbG90KGhlcmUoImZpZ3VyZXNfcGRmL3YyLzVfY292ZXJhZ2UucGRmIiksIHBsdF9yZWFkX2FjY3VyYWN5X3N1YnNldCwgYmFzZV9oZWlnaHQgPSA0LCBiYXNlX3dpZHRoID0gNikKYGBgCgpgYGB7cn0KcGx0X3JlYWRfYWxsZWxlX2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYgJT4lIAogICAgZmlsdGVyKGxvY3VzICVpbiUgbG9jaSkgJT4lIAogICAgZmlsdGVyKCEoZ2Vub3R5cGVyID09ICJwaGxhdCIgJiBsb2N1cyA9PSAiRFBCMSIpKSAlPiUgCiAgICBmaWx0ZXIoIShnZW5vdHlwZXIgPT0gIm9wdGl0eXBlIiAmIGxvY3VzID09ICJEUEIxIikpICU+JSAKICAgIGZpbHRlcighKGdlbm90eXBlciA9PSAib3B0aXR5cGUiICYgbG9jdXMgPT0gIkRRQTEiKSkgJT4lIAogICAgZmlsdGVyKCEoZ2Vub3R5cGVyID09ICJvcHRpdHlwZSIgJiBsb2N1cyA9PSAiRFJCMSIpKSwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJuX2FsbGVsZXMiLCBmYWNldF9nZW5vdHlwZXIgPSBUKQpwbHRfcmVhZF9hbGxlbGVfYWxsCmBgYApgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodD03fQpwbHRfbWFpbiA8LSBwbG90X2dyaWQoCiAgcGx0X3JlYWRfYWNjdXJhY3lfc3Vic2V0LAogIHBsdF9yZWFkX2FsbGVsZV9hbGwsCiAgbmNvbCA9IDEsCiAgcmVsX2hlaWdodHMgPSBjKDMsMiksCiAgYWxpZ24gPSAidiIsCiAgYXhpcyA9ICJsciIsCiAgbGFiZWxzID0gYygiQSIsICJCIikKKQpwbHRfbWFpbgpgYGAKYGBge3J9CnNhdmVfcGxvdChoZXJlKCJmaWd1cmVzX3BkZi92Mi81X2NvdmVyYWdlLnBkZiIpLCBwbHRfbWFpbiwgYmFzZV9oZWlnaHQgPSA3LCBiYXNlX3dpZHRoID0gNykKYGBgCgojIFNsb3BlIHN0YXRzCgpgYGB7cn0KZ2V0X2xtX3N0YXRzIDwtIGZ1bmN0aW9uKGRmLCB4X3ZhciA9ICJjb3ZlcmFnZSIsIHlfdmFyID0gImFjY3VyYWN5IiwgZmllbGRfdmFyID0gImZpZWxkXzIiLCB4X2xvZyA9IFQpewogICMgSW5wdXQgY2hlY2tzCiAgYXJnX2NvbCA8LSBtYWtlQXNzZXJ0Q29sbGVjdGlvbigpCiAgYXNzZXJ0Q2hvaWNlKHlfdmFyLCBjKCJhY2N1cmFjeSIsICJzdWNjZXNzIiwgIm5fYWxsZWxlcyIpLCBhZGQgPSBhcmdfY29sKQogIGFzc2VydENob2ljZSh4X3ZhciwgYygicmVhZHMiLCAiY292ZXJhZ2UiLCAibl9jZWxscyIpLCBhZGQgPSBhcmdfY29sKQogIGFzc2VydENob2ljZShmaWVsZF92YXIsIGMoImZpZWxkXzEiLCAiZmllbGRfMiIsICJmaWVsZF8zIiksIGFkZCA9IGFyZ19jb2wpCiAgYXNzZXJ0TG9naWNhbCh4X2xvZywgYWRkID0gYXJnX2NvbCkKICBpZiAoYXJnX2NvbCRpc0VtcHR5KCk9PUYpIHttYXAoYXJnX2NvbCRnZXRNZXNzYWdlcygpLHByaW50KTtyZXBvcnRBc3NlcnRpb25zKGFyZ19jb2wpfQogIAogICMgYnJvd3NlcigpCiAgZGYgPC0gZGYgJT4lIAogICAgZmlsdGVyKGZpZWxkID09IGZpZWxkX3ZhcikgJT4lIAogICAgZXhjbHVkZV9nZW5vdHlwZXJfZmllbGRzKCkgIyBSZXBsYWNlIHcvIGV4Y2x1ZGVfZ2Vub3R5cGVyX2ZpZWxkcz8KICBpZiAoeF9sb2cgPT1UKXtkZiA8LSBkZiAlPiUgbXV0YXRlX2F0KHhfdmFyLCBsb2cxMCl9CiAgZGYgJT4lCiAgZmlsdGVyX2F0KHhfdmFyLCBmdW5jdGlvbih4KSAhaXMubmEoeCkgJiAhaXMuaW5maW5pdGUoeCkpICU+JSAKICBncm91cF9ieShsb2N1cywgZmllbGQsIGdlbm90eXBlcikgJT4lCiAgbmVzdCgpICU+JQogIG11dGF0ZShkYXRhID0gbWFwKGRhdGEsIGZ1bmN0aW9uKHgpewogICAgc3VtbWFyeShsbSghIXN5bSh5X3ZhcikgfiAhIXN5bSh4X3ZhciksIGRhdGEgPSB4KSkgJT4lIGJyb29tOjp0aWR5KCkgJT4lIGZpbHRlcih0ZXJtID09IHhfdmFyKQogIH0pKSAlPiUgdW5uZXN0X3dpZGVyKGRhdGEpICU+JQogIG11dGF0ZShlc3RfbWluID0gcm91bmQoZXN0aW1hdGUgLSAyKnN0ZC5lcnJvciwgMiksCiAgICAgICAgIGVzdF9tYXggPSByb3VuZChlc3RpbWF0ZSArIDIqc3RkLmVycm9yLCAyKSkKfQoKbW9kZWxfc3RhdHMgPC0gZ2V0X2xtX3N0YXRzKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIsIHhfbG9nID0gVCkKbW9kZWxfc3RhdHMKYGBgCgpgYGB7cn0KbW9kZWxfc3RhdHMgPC0gYmluZF9yb3dzKAogIGdldF9sbV9zdGF0cyhzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiLCB4X2xvZyA9IFQpICU+JSBtdXRhdGUoeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpLAogIGdldF9sbV9zdGF0cyhzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAic3VjY2VzcyIsIHhfbG9nID0gVCkgJT4lIG11dGF0ZSh4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gInN1Y2Nlc3MiKSwKICBnZXRfbG1fc3RhdHMoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAiYWNjdXJhY3kiLCB4X2xvZyA9IFQpICU+JSBtdXRhdGUoeF92YXIgPSAiY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpLAogIGdldF9sbV9zdGF0cyhzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJzdWNjZXNzIiwgeF9sb2cgPSBUKSAlPiUgbXV0YXRlKHhfdmFyID0gImNlbGxzIiwgeV92YXIgPSAic3VjY2VzcyIpCikgICU+JSAKICAjIGZpbHRlcihncmVwbCgiXltBQkNdIiwgbG9jdXMpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIoZmllbGQgPT0gImZpZWxkXzIiKSAKYGBgCgoKCiMgVGFibGUKYGBge3J9CmNvbF92YXJzIDwtIGMoInhfdmFyIiwgImxvY3VzIikKcm93X3ZhcnMgPC0gYygieV92YXIiLCAiZ2Vub3R5cGVyIikKCmZsZXhfZGYgPC0gbW9kZWxfc3RhdHMgJT4lIAogICMgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcihmaWVsZCA9PSAiZmllbGRfMiIpICU+JSAKICBtdXRhdGUoQ0kgPSBzcHJpbnRmKCIoJXMpIC0gKCVzKSIsIGVzdF9taW4sIGVzdF9tYXgpKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lIAogIHNlbGVjdChnZW5vdHlwZXIsIGxvY3VzLCBDSSwgeF92YXIsIHlfdmFyKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNvbF92YXJzLCB2YWx1ZXNfZnJvbSA9ICJDSSIsIG5hbWVzX3NlcCA9ICAiLSIpIAoKZGZfa2V5IDwtIHRpYmJsZShjb2xfa2V5cyA9IG5hbWVzKGZsZXhfZGYpKSAlPiUgCiAgZmlsdGVyKCEoY29sX2tleXMgJWluJSBjKGNvbF92YXJzLCByb3dfdmFycykpKSAlPiUgCiAgc2VwYXJhdGUoY29sX2tleXMsIGludG8gPSBjb2xfdmFycywgc2VwID0gIi0iLCByZW1vdmUgPSBGKSAlPiUKICBtdXRhdGVfYXQodmFycyhjb250YWlucygiZ2Vub3R5cGVyIikpLCByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKSAlPiUgCiAgYXJyYW5nZV9hdChjb2xfdmFycykgJT4lIAogIG11dGF0ZV9hbGwoYXMuY2hhcmFjdGVyKSAlPiUgCiAgbXV0YXRlX2F0KGNvbF92YXJzWyEoY29sX3ZhcnMgJWluJSAibG9jdXMiKV0sIHN0cl90b19zZW50ZW5jZSkKCm91dHB1dF90YWJsZSA8LSBmbGV4X2RmICU+JSAKICBzZWxlY3Qocm93X3ZhcnMsIGV2ZXJ5dGhpbmcoKSkgJT4lIAogIG11dGF0ZV9hdChyb3dfdmFyc1shKHJvd192YXJzICVpbiUgImdlbm90eXBlciIpXSwgc3RyX3RvX3NlbnRlbmNlKSAlPiUgCiAgIyBzZWxlY3QobG9jdXMsIGRmX2tleSRjb2xfa2V5cykgJT4lIAogIGZsZXh0YWJsZSgpICU+JSAKICBjb2xmb3JtYXRfY2hhcihuYV9zdHIgPSAiLS0tIikgJT4lCiAgbWVyZ2VfdihqPTE6MikgJT4lIAogIHNldF9oZWFkZXJfZGYobWFwcGluZyA9IGRmX2tleSwga2V5ID0gImNvbF9rZXlzIikgJT4lIAogIG1lcmdlX2gocGFydCA9ICJoZWFkZXIiKSAlPiUKICB0aGVtZV92YW5pbGxhKCkgJT4lIAogICMgdmxpbmUoaj12bGluZXNfc2VxdWVuY2UsIGJvcmRlciA9IGZwX2JvcmRlcl9kZWZhdWx0KCkpICU+JQogIGZpeF9ib3JkZXJfaXNzdWVzKCkgJT4lIAogIGF1dG9maXQoKSAlPiUgCiAgZm9udHNpemUoc2l6ZSA9IDgsIHBhcnQgPSAiYWxsIikgCgpwcmludChvdXRwdXRfdGFibGUpCiMgb3V0cHV0X3RhYmxlICU+JSBwcmludChwcmV2aWV3ID0gInBwdHgiKQpgYGAKCgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAgIGRmIDwtIGRmICU+JSAgLS0+CjwhLS0gICAgIG11dGF0ZShmaWVsZCA9IHJlZm9ybWF0X2hsYV9maWVsZChmaWVsZCkpICU+JSAgLS0+CjwhLS0gICAgIG11dGF0ZV9hdCh2YXJzKGNvbnRhaW5zKCJnZW5vdHlwZXIiKSksIHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIpICU+JSAgLS0+CjwhLS0gICAgIG11dGF0ZShjZWxsX3ZhbHVlID0gaWZlbHNlKCFpcy5uYShzZSksICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByaW50ZigiJXMgJXMgJXMiLCByb3VuZChtZWFuX2FjY3VyYWN5LDIpLCJcdTAwQjEiLHJvdW5kKHNlLDIpKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmludGYoIiVzIiwgcm91bmQobWVhbl9hY2N1cmFjeSwyKSkpKSAlPiUgLS0+CjwhLS0gICAgIG11dGF0ZShjZWxsX3ZhbHVlID0gaWZlbHNlKGdyZXBsKCJOQSIsIGNlbGxfdmFsdWUpLCBOQSwgY2VsbF92YWx1ZSkpICU+JSAgLS0+CjwhLS0gICAgIHNlbGVjdCgtc2QsLXNlLCAtbWVhbl9hY2N1cmFjeSkgJT4lICAtLT4KPCEtLSAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG5lc3RpbmdfdmFycywgdmFsdWVzX2Zyb20gPSAiY2VsbF92YWx1ZSIsIG5hbWVzX3NlcCA9ICItIikgIC0tPgoKPCEtLSAgIGRmX2tleSA8LSBzdXBwcmVzc1dhcm5pbmdzKHt0aWJibGUoY29sX2tleXMgPSBuYW1lcyhkZikpICU+JSAgLS0+CjwhLS0gICAgICAgZmlsdGVyKCFncmVwbCgibG9jdXMiLCBjb2xfa2V5cykpICU+JSAgLS0+CjwhLS0gICAgICAgc2VwYXJhdGUoY29sX2tleXMsIGludG8gPSBuZXN0aW5nX3ZhcnMsIHNlcCA9ICItIiwgcmVtb3ZlID0gRikgJT4lIC0tPgo8IS0tICAgICAgIG11dGF0ZV9hdCh2YXJzKGNvbnRhaW5zKCJnZW5vdHlwZXIiKSksIHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIpICU+JSAgLS0+CjwhLS0gICAgICAgYXJyYW5nZV9hdChuZXN0aW5nX3ZhcnMpICU+JSAgLS0+CjwhLS0gICAgICAgbXV0YXRlX2FsbChhcy5jaGFyYWN0ZXIpIC0tPgo8IS0tICAgfSkgLS0+Cgo8IS0tICAgdmxpbmVfdmFyIDwtIHRhaWwobmVzdGluZ192YXJzLDEpIC0tPgo8IS0tICAgdmxpbmVzX3NlcXVlbmNlIDwtIHNlcSgxLG5yb3coZGZfa2V5KSxieSA9IGxlbmd0aCh1bmlxdWUoZGZfa2V5W1t2bGluZV92YXJdXSkpKSAtLT4KPCEtLSAgIGRmIDwtIGRmICU+JSAgLS0+CjwhLS0gICAgIHNlbGVjdChsb2N1cywgZGZfa2V5JGNvbF9rZXlzKSAlPiUgIC0tPgo8IS0tICAgICBmbGV4dGFibGUoKSAlPiUgIC0tPgo8IS0tICAgICBjb2xmb3JtYXRfY2hhcihuYV9zdHIgPSAiLS0tIikgJT4lICAtLT4KPCEtLSAgICAgc2V0X2hlYWRlcl9kZihtYXBwaW5nID0gZGZfa2V5LCBrZXkgPSAiY29sX2tleXMiKSAlPiUgIC0tPgo8IS0tICAgICBtZXJnZV9oKHBhcnQgPSAiaGVhZGVyIikgJT4lICAtLT4KPCEtLSAgICAgdGhlbWVfdmFuaWxsYSgpICU+JSAgLS0+CjwhLS0gICAgIHZsaW5lKGo9dmxpbmVzX3NlcXVlbmNlLCBib3JkZXIgPSBmcF9ib3JkZXJfZGVmYXVsdCgpKSAlPiUgLS0+CjwhLS0gICAgIGZpeF9ib3JkZXJfaXNzdWVzKCkgLS0+CjwhLS0gICByZXR1cm4oZGYpIC0tPgo8IS0tIGBgYCAtLT4KCgoKCgojIFNjcmF0Y2ggd29yawoKPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0gLS0+CjwhLS0gc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSAgLS0+CjwhLS0gICBmaWx0ZXIoZmllbGQgPT0gImZpZWxkXzIiKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKG5fYWxsZWxlcyA9IGZhY3RvcihuX2FsbGVsZXMsIGxldmVscyA9IDA6MikpICU+JSAgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHggPSBuX2FsbGVsZXMsIHkgPSBsb2cxMChuX2NlbGxzKSkpKyAtLT4KPCEtLSAgIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIikrIC0tPgo8IS0tICAgZ2VvbV9qaXR0ZXIoc2l6ZSA9IDAuMiwgaGVpZ2h0ID0gMCwgd2lkdGggPTAuMDUsIGFscGhhID0gMC4yKSsgLS0+CjwhLS0gICAjIGdlb21fYm94cGxvdCh3aWR0aD0wLjEpKyAtLT4KPCEtLSAgICMgZ2diZWVzd2FybTo6Z2VvbV9iZWVzd2FybShzaXplID0gMC4xLCBhbHBoYSA9IDAuMSkrIC0tPgo8IS0tICAgY29vcmRfZmxpcCgpKyAtLT4KPCEtLSAgIGZhY2V0X2dyaWQoZ2Vub3R5cGVyIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIikrIC0tPgo8IS0tICAgdGhlbWVfYncoKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gIyBDb3ZlcmFnZSBzdGF0cyAtLT4KPCEtLSBgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0gLS0+CjwhLS0gc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSAgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHggPSBuX2NlbGxzLCB5ID0gY292ZXJhZ2UsIGNvbG9yID0gZ2Vub3R5cGVyKSkgKyAgLS0+CjwhLS0gICBnZW9tX3BvaW50KCkgKyAtLT4KPCEtLSAgIGZhY2V0X3dyYXAoZ2Vub3R5cGVyIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIiwgbnJvdyA9IDMpICsgIC0tPgo8IS0tICAgdGhlbWVfYncoKSArIC0tPgo8IS0tICAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgLS0+CjwhLS0gICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9IC0tPgo8IS0tIHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiAlPiUgIC0tPgo8IS0tICAgc2VsZWN0KC1nZW5vdHlwZXIpICU+JSBkaXN0aW5jdCgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoY292X3Blcl9jZWxsID0gY292ZXJhZ2Uvbl9jZWxscykgJT4lICAtLT4KPCEtLSAgIGdncGxvdChhZXMoeCA9IGxvZzEwKG5fY2VsbHMpLCB5ID0gY292X3Blcl9jZWxsKSkgKyAgLS0+CjwhLS0gICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjEpICsgLS0+CjwhLS0gICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSsgLS0+CjwhLS0gICAjIHN0YXRfc21vb3RoKCkrIC0tPgo8IS0tICAgICBmYWNldF93cmFwKCB+IGxvY3VzLCBzY2FsZXMgPSAiZnJlZSIsIG5yb3cgPSAxKSArIC0tPgo8IS0tICAgdGhlbWVfYncoKSArIC0tPgo8IS0tICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkrIC0tPgo8IS0tICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9Niwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfSAtLT4KPCEtLSBzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYgJT4lICAtLT4KPCEtLSAgIHNlbGVjdCgtZ2Vub3R5cGVyKSAlPiUgZGlzdGluY3QoKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKCFpcy5uYShuX2NlbGxzKSkgJT4lICAtLT4KPCEtLSAgIG11dGF0ZShjb3ZfcGVyX2NlbGwgPSBjb3ZlcmFnZS9uX2NlbGxzKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKG5fY2VsbHMgPSBjdXQobl9jZWxscywgc2VxKDAsMTAwMDAsYnk9MjUwMCksIHJpZ2h0ID0gRikpICU+JSAgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHggPSBuX2NlbGxzLCB5ID0gY292X3Blcl9jZWxsKSkgKyAgLS0+CjwhLS0gICBzdGF0X3N1bW1hcnkoZnVuID0gZnVuY3Rpb24oeCkgbWVhbih4LG5hLnJtPVQpLCBnZW9tID0gImJhciIpICsgLS0+CjwhLS0gICAjIGdlb21fcG9pbnQoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMSkgKyAtLT4KPCEtLSAgICMgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIikrIC0tPgo8IS0tICAgIyBzdGF0X3Ntb290aCgpKyAtLT4KPCEtLSAgIGZhY2V0X3dyYXAoIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIiwgbnJvdyA9IDEpICsgLS0+CjwhLS0gICB0aGVtZV9idygpICsgLS0+CjwhLS0gICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gYGBge3J9IC0tPgo8IS0tIGhpc3QoY3V0KDE6MTAwLCBzZXEoMSwxMDAsMTApKSkgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSAgLS0+CjwhLS0gICB1bmdyb3VwKCkgJT4lICAtLT4KPCEtLSAgIGNvdW50KGdlbm90eXBlciwgbG9jdXMsIGZpZWxkKSAtLT4KPCEtLSBgYGAgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiAlPiUgIC0tPgo8IS0tICAgdW5ncm91cCgpICU+JSAgLS0+CjwhLS0gICBjb3VudChnZW5vdHlwZXIsIGxvY3VzLCBmaWVsZCkgLS0+CjwhLS0gYGBgIC0tPgo=